home *** CD-ROM | disk | FTP | other *** search
Wrap
// BEGIN FLOCK GPL // // Copyright Flock Inc. 2005-2007 // http://flock.com // // This file may be used under the terms of of the // GNU General Public License Version 2 or later (the "GPL"), // http://www.gnu.org/licenses/gpl.html // // Software distributed under the License is distributed on an "AS IS" basis, // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License // for the specific language governing rights and limitations under the // License. // // END FLOCK GPL // const ENABLE_DEBUG = true; // switch to turn off slow debug code for production function DEBUG(x) { if (ENABLE_DEBUG) debug("flockMagnoliaService: "+x+"\n"); } const Cc = Components.classes; const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; const CLASS_ID = Components.ID ('{7c9aa278-85ed-4cbe-b0bc-f35df0978ade}'); const CLASS_NAME = "Flock Magnolia Service"; const CONTRACT_ID = "@flock.com/magnolia-service;1"; const SERVICE_ENABLED_PREF = "flock.service.magnolia.enabled"; const CATEGORY_COMPONENT_NAME = "Magnolia JS Component" const CATEGORY_ENTRY_NAME = "magnolia" const RDFS = Cc['@mozilla.org/rdf/rdf-service;1'].getService (Ci.nsIRDFService); var gCompTK; function getCompTK() { if (!gCompTK) { gCompTK = Cc["@flock.com/singleton;1"].getService(Ci.flockISingleton) .getSingleton("chrome://browser/content/flock/services/common/load-compTK.js") .wrappedJSObject; } return gCompTK; } var gTimers = []; // For use with the scheduler const MAGNOLIA_FAVICON = 'http://ma.gnolia.com/favicon.ico'; function loadLibraryFromSpec(aSpec) { var loader = Cc['@mozilla.org/moz/jssubscript-loader;1'] .getService(Ci.mozIJSSubScriptLoader); loader.loadSubScript(aSpec); } loadLibraryFromSpec("chrome://browser/content/flock/favorites/deliciousApi.js"); loadLibraryFromSpec("chrome://browser/content/flock/favorites/onlineFavoritesBackend.js"); function stringify (foo) { if (foo==null) { return ''; } else { return foo; } } function dateify (foo) { if (foo == null) { return 0; } else { return foo.getTime (); } } function sanitize_tags (tags) { return tags?tags.split(/[\s,]/).sort().join(','):''; } function bookmarks_match (bm1, bm2) { return (bm1.private == bm2.private && /* booleans - nice and easy */ stringify (bm1.name) == stringify (bm2.name) && stringify (bm1.description) == stringify (bm2.description) && sanitize_tags(bm1.tags) == sanitize_tags(bm2.tags) && dateify (bm1.datevalue) == dateify (bm2.datevalue)); } function flockMagnoliaService() { var obs = Cc["@mozilla.org/observer-service;1"].getService (Ci.nsIObserverService); obs.addObserver (this, 'xpcom-shutdown', false); this._sync_worker_timer = null; this._state = 'idle'; this.status = Ci.flockIWebService.STATUS_UNKNOWN; this.acUtils = Cc["@flock.com/account-utils;1"].getService(Ci.flockIAccountUtils); this.url = "http://ma.gnolia.com"; this.api = new DeliciousAPI('https://ma.gnolia.com/api/mirrord/v1/'); this.mIsInitialized = false; this._ctk = { interfaces: [ "nsISupports", "nsISupportsCString", "nsIClassInfo", "nsIObserver", "nsITimerCallback", "flockIWebService", "flockIManageableWebService", "flockIBookmarkWebService", "flockIPollingService", ], shortName: "magnolia", fullName: "Magnolia", description: CLASS_NAME, favicon: MAGNOLIA_FAVICON, CID: CLASS_ID, contractID: CONTRACT_ID, accountClass: flockMagnoliaAccount }; this._logger = Cc["@flock.com/logger;1"].createInstance (Ci.flockILogger); this._logger.init ("magnolia"); this._profiler = Cc["@flock.com/profiler;1"].getService(Ci.flockIProfiler); this.init(); } flockMagnoliaService.prototype.init = function flockMagnoliaService_init () { // Prevent re-entry if (this.mIsInitialized) return; this.mIsInitialized = true; var evtID = this._profiler.profileEventStart("magnolia-init"); this._logger.info(".init()"); // Determine whether this service has been disabled, and unregister if so. this.prefService = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); if ( this.prefService.getPrefType(SERVICE_ENABLED_PREF) && !this.prefService.getBoolPref(SERVICE_ENABLED_PREF) ) { this._logger.info("Pref "+SERVICE_ENABLED_PREF+" set to FALSE... not initializing."); var catMgr = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager); catMgr.deleteCategoryEntry("wsm-startup", CATEGORY_COMPONENT_NAME, true); catMgr.deleteCategoryEntry("flockWebService", CATEGORY_ENTRY_NAME, true); return; } // get a coop interface to it this._coop = Cc['@flock.com/singleton;1'].getService(Ci.flockISingleton).getSingleton('chrome://browser/content/flock/common/load-faves-coop.js').wrappedJSObject; if (this._coop.Service.exists ('urn:magnolia:service')) { this._service = this._coop.get ('urn:magnolia:service'); } else { this._service = new this._coop.Service ('urn:magnolia:service', { name: 'magnolia', desc: 'Magnolia' }); } this._service.serviceId = CONTRACT_ID; // Load Web Detective file this.webDetective = this.acUtils.useWebDetective("magnolia.xml"); this._service.domains = this.webDetective.getString("magnolia", "domains", "gnolia.com"); // Now that we know the service URN, we can register this service // with the Web Service Manager. this.urn = this._service.id(); this.bookmarksRootUrn = "urn:magnolia:bookmarks:"; this._profiler.profileEventEnd(evtID, ""); } // BEGIN flockIWebService interface flockMagnoliaService.prototype.title = 'Magnolia'; flockMagnoliaService.prototype.icon = MAGNOLIA_FAVICON; flockMagnoliaService.prototype.addAccountById = function flockMagnoliaService_addAccountById(aAccountID, aIsTransient, aListener) { this._logger.debug("{flockIWebService}.addAccountById('"+aAccountID+"', "+aIsTransient+", aListener)"); var acct = onlineFavoritesBackend.createAccount(this, aAccountID, aIsTransient); if (aListener) aListener.onSuccess(acct, "addAccount"); return acct; } flockMagnoliaService.prototype.removeAccount = function flockMagnoliaService_removeAccount(aUrn) { this._logger.debug("{flockIWebService}.removeAccount('"+aUrn+"')"); onlineFavoritesBackend.removeAccount(this, aUrn); } // END flockIWebService interface // BEGIN flockIManageableWebService interface flockMagnoliaService.prototype.updateAccountStatusFromDocument = function flockMagnoliaService_updateAccountStatusFromDocument(aDocument) { this._logger.debug("{flockIManageableWebService}.updateAccountStatusFromDocument()"); if (this.webDetective.detect("magnolia", "loggedout", aDocument, null)) { this.acUtils.markAllAccountsAsLoggedOut(CONTRACT_ID); } else if (this.webDetective.detect("magnolia", "loggedin", aDocument, null)) { var results = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag2); if (this.webDetective.detect("magnolia", "accountinfo", aDocument, results)) { var accountID = results.getPropertyAsAString("accountid"); var accountURN = this.acUtils.getAccountURNById(this.urn, accountID); this.acUtils.ensureOnlyAuthenticatedAccount(accountURN); } } } // END flockIManageableWebService interface flockMagnoliaService.prototype._getAllTags = function (aUrn, aAccountId, aPassword, aListener) { this._logger.info ('getAllTags(...)\n'); var svc = this; this.api.tagsGet ({ onError: function () { // FIXME: report error? /* BUG: 5705 */ svc._logger.error ('Delicious tags/get failed (in Magnolia)\n'); if (aListener) aListener.onError (); }, onSuccess: function (aResult, aTopic) { // Delete existing tags and insert the new ones - there is probably a more efficient way to do it var localBookmarks = svc._coop.get(svc.urn+':'+aAccountId+':bookmarks'); tagenum = localBookmarks.tag.enumerate(); while (tagenum.hasMoreElements()) { var tag = tagenum.getNext(); localBookmarks.tag.remove(tag); } for (var i=0; i<aResult.length; i++) { localBookmarks.tag.addOnce(aResult[i].tag); } // Tell the poller we're done if (aListener) aListener.onResult(); } }); } // flockIBookmarkWebService flockMagnoliaService.prototype.publish = function flockMagnoliaService_publish (aListener, aAccountId, aBookmark, aPrivate) { this._logger.info("Publish ("+aBookmark.URL+","+aBookmark.name+") to "+aAccountId+"@Magnolia\n"); var accountUrn = this.urn+':'+aAccountId; var password = this.acUtils.getPassword (accountUrn); var svc = this; var args = { url: aBookmark.URL, description: aBookmark.name, extended: aBookmark.description, tags: sanitize_tags(aBookmark.tags) }; if (aPrivate) args.shared = 'no'; this.api.call ('posts/add', args, { onError: function () { // FIXME: report error? /* BUG: 5705 */ svc._logger.error ('Magnolia posts/add failed\n'); if (aListener) aListener.onError (null, null, null); }, onSuccess: function (aXML) { if (!aXML || !aXML.documentElement || aXML.documentElement.tagName != 'result') { // bad posts/all response // FIXME: report error? /* BUG: 5705 */ svc._logger.error ('Magnolia posts/add failed - invalid xml response'); if (aListener) aListener.onError (null, null, null); return; } var result = aXML.documentElement.getAttribute("code"); svc._logger.debug ("Result to posts/add: "+result); if (result == "done") { var localBookmarks = svc._coop.get(svc.urn+":"+aAccountId+":bookmarks"); var tags = aBookmark.tags.split(" "); for (i in tags) localBookmarks.tag.addOnce(tags[i]); onlineFavoritesBackend.updateBookmark (svc, accountUrn, aBookmark, aPrivate); if (aListener) aListener.onSuccess(null, null); } else { if (aListener) aListener.onError(null, null, null); } } }, password); } // flockIBookmarkWebService flockMagnoliaService.prototype.publishList = function (aListener, aAccountId, aBookmarkList, aPrivate) { var delay = 1000; // 1 second between each query var svc = this; this._publishTimer = []; var i = 0; var sync = []; var bookmarks = []; var sync = { notify: function (timer) { var bm = bookmarks.shift(); svc.publish(aListener, aAccountId, bm, aPrivate); } } while (aBookmarkList.hasMoreElements()) { var bookmark = aBookmarkList.getNext().QueryInterface(Ci.flockIBookmark); // Duplicate it because it's going to be removed too early bookmarks[i] = {}; bookmarks[i].URL = bookmark.URL; bookmarks[i].name = bookmark.name; bookmarks[i].description = bookmark.description; bookmarks[i].tags = bookmark.tags; bookmarks[i].time = bookmark.time; this._logger.debug("==== Set a timer to "+i*delay+" for "+bookmarks[i].URL+"\n"); this._publishTimer[i] = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer); this._publishTimer[i].initWithCallback(sync, i * delay, Ci.nsITimer.TYPE_ONE_SHOT); i++; } } // flockIBookmarkWebService flockMagnoliaService.prototype.remove = function (aListener, aAccountId, aUrl) { this._logger.info("Remove "+aUrl+" from "+aAccountId+"@Magnolia\n"); var password = this.acUtils.getPassword (this.urn+':'+aAccountId); var svc = this; var args = { url: aUrl }; this.api.call ('posts/delete', args, { onError: function () { // FIXME: report error? /* BUG: 5705 */ svc._logger.error ('Magnolia posts/delete failed\n'); if (aListener) aListener.onError (null, null, null); }, onSuccess: function (aXML) { if (!aXML || !aXML.documentElement || aXML.documentElement.tagName != 'result') { // bad posts/all response // FIXME: report error? /* BUG: 5705 */ svc._logger.error ('Magnolia posts/delete failed - invalid xml response'); if (aListener) aListener.onError (null, null, null); return; } var result = aXML.documentElement.getAttribute("code"); svc._logger.debug ("Result to posts/delete: "+result); if (result == "done") { onlineFavoritesBackend.destroyBookmark (svc, aAccountId, aUrl); Cc["@flock.com/poller-service;1"].getService(Ci.flockIPollerService) .forceRefresh(this.urn+":"+aAccountId+":bookmarks"); if (aListener) aListener.onSuccess(null, null); } else { if (aListener) aListener.onError(null, null, null); } } }, password); } // flockIBookmarkWebService flockMagnoliaService.prototype.exists = function flockMagnoliaService_publish (aAccountId, aURL) { return this._coop.Bookmark.exists (this.urn+":"+aAccountId+":"+aURL); } flockMagnoliaService.prototype.getUserUrl = function (aAccountId) { return "http://ma.gnolia.com/people/"+aAccountId; } // flockIPollingService flockMagnoliaService.prototype.refresh = function flockMagnoliaService_refresh (aURN, aListener) { var svc = this; this._logger.info ('refresh (' + aURN + ')'); if (!this._coop.OnlineBookmarksStream.exists (aURN)) throw "flockMagnoliaService: "+aURN+" can't be found"; var bookmarks = this._coop.get (aURN); var accountId = bookmarks.userid; var accountUrn = this.urn + ":" + accountId; // nsIPassword for auth for this sync var password = this.acUtils.getPassword (accountUrn); this.api.postsAllIfNewer({ onError: function (aError) { svc._logger.error ('Magnolia (Delicious API) posts/all failed: [' +aError.errorCode+"] "+aError.errorString+" ([" +aError.serviceErrorCode+"] "+ aError.serviceErrorString+")"); var message; switch (aError.errorCode) { case aError.FAVS_UNAVAILABLE: message = "Ma.gnolia.com reported that the service is currently unavailable; your bookmarks cannot be updated. Contact Magnolia for more informations."; break; case aError.FAVS_INVALID_AUTH: message = "Ma.gnolia.com reported that your username or password is wrong. Please log in to Ma.gnolia.com again to update your authentication information."; break; default: message = "Ma.gnolia.com returned an unknown error ["+aError.serviceErrorCode+"]"; } onlineFavoritesBackend.showNotification(message); if (aListener) aListener.onError(aError); }, onSuccess: function (aSubject, aTopic) { if (aTopic == "updated") { onlineFavoritesBackend.updateLocal(svc, aSubject, accountUrn); // Now refresh the tag list (wait one sec) var sync = { notify: function (timer) { svc._getAllTags (aURN, accountId, password, aListener); } } svc._timer = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer); svc._timer.initWithCallback(sync, 1000, Ci.nsITimer.TYPE_ONE_SHOT); } else { // Tell the poller we're done if (aListener) aListener.onResult(); } } } , password, bookmarks.last_update_time); } // nsIObserver flockMagnoliaService.prototype.observe = function (subject, topic, state) { switch (topic) { case 'xpcom-shutdown': var obs = Cc["@mozilla.org/observer-service;1"] .getService (Ci.nsIObserverService); obs.removeObserver (this, 'xpcom-shutdown'); return; } } /****************************************************************************** * XPCOM Functions for construction and registration ******************************************************************************/ function createModule(aParams) { return { registerSelf: function (aCompMgr, aFileSpec, aLocation, aType) { aCompMgr.QueryInterface(Ci.nsIComponentRegistrar); aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName, aParams.contractID, aFileSpec, aLocation, aType ); var catMgr = Cc["@mozilla.org/categorymanager;1"] .getService(Ci.nsICategoryManager); if (!aParams.categories) { aParams.categories = []; } for (var i = 0; i < aParams.categories.length; i++) { var cat = aParams.categories[i]; catMgr.addCategoryEntry( cat.category, cat.entry, cat.value, true, true ); } }, getClassObject: function (aCompMgr, aCID, aIID) { if (!aCID.equals(aParams.CID)) { throw Cr.NS_ERROR_NO_INTERFACE; } if (!aIID.equals(Ci.nsIFactory)) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; } return { // Factory createInstance: function (aOuter, aIID) { if (aOuter != null) { throw Cr.NS_ERROR_NO_AGGREGATION; } var comp = new aParams.componentClass(); if (aParams.implementationFunc) { aParams.implementationFunc(comp); } return comp.QueryInterface(aIID); } }; }, canUnload: function (aCompMgr) { return true; } }; } // NS Module entrypoint function NSGetModule(aCompMgr, aFileSpec) { return createModule({ componentClass: flockMagnoliaService, CID: CLASS_ID, contractID: CONTRACT_ID, componentName: CATEGORY_COMPONENT_NAME, implementationFunc: function (aComp) { getCompTK().addAllInterfaces(aComp); }, categories: [ { category: "wsm-startup", entry: CATEGORY_COMPONENT_NAME, value: CONTRACT_ID }, { category: "flockWebService", entry: CATEGORY_ENTRY_NAME, value: CONTRACT_ID } ] }); } // ======================================================= // ========== BEGIN flockMagnoliaAccount class ========== // ======================================================= function flockMagnoliaAccount() { this._coop = Cc['@flock.com/singleton;1'].getService(Ci.flockISingleton) .getSingleton('chrome://browser/content/flock/common/load-faves-coop.js') .wrappedJSObject; this.acUtils = Cc["@flock.com/account-utils;1"].getService(Ci.flockIAccountUtils); this.service = Cc[CONTRACT_ID].getService(Ci.flockIWebService); this._ctk = { interfaces: [ "nsISupports", "flockIWebServiceAccount", "flockIBookmarkWebServiceAccount" ] }; this._logger = Cc["@flock.com/logger;1"].createInstance (Ci.flockILogger); this._logger.init ("magnolia"); getCompTK().addAllInterfaces(this); } // BEGIN flockIWebServiceAccount interface flockMagnoliaAccount.prototype.activate = function flockMagnoliaAccount_activate(aListener) { this._logger.debug("{flockIWebServiceAccount}.activate()"); var acctCoopObj = this._coop.get(this.urn); acctCoopObj.isAuthenticated = true; this._coop.get(this.urn + ':bookmarks').isPollable = true; } flockMagnoliaAccount.prototype.keep = function flockMagnoliaAccount_keep(aListener) { this._logger.debug("{flockIWebServiceAccount}.keep()"); this._coop.get(this.urn).isTransient = false; this._coop.get(this.urn+":bookmarks").isTransient = false; this.acUtils.makeTempPasswordPermanent(this.urn); if (aListener) { aListener.onSuccess(this, "keep"); } } // END flockIWebServiceAccount interface